package com.example.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.common.aliyun.GreenImageScan;
import com.example.common.aliyun.GreenTextScan;
import com.example.common.constants.ArticleConstants;
import com.example.common.tess4j.Tess4jClient;
import com.example.common.util.R;
import com.example.common.util.ResultCodeRecord;
import com.example.feignapi.article.ArticleClient;
import com.example.file.service.FileStorageService;
import com.example.model.article.dto.ArticleDto;
import com.example.model.wemedia.pojo.Channel;
import com.example.model.wemedia.pojo.News;
import com.example.model.wemedia.pojo.Sensitive;
import com.example.model.wemedia.pojo.User;
import com.example.utils.common.SensitiveWordUtil;
import com.example.wemedia.mapper.ChannelMapper;
import com.example.wemedia.mapper.NewsMapper;
import com.example.wemedia.mapper.SensitiveMapper;
import com.example.wemedia.mapper.UserMapper;
import com.example.wemedia.service.NewsAutoScanService;
import com.example.wemedia.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class NewsAutoScanServiceImpl implements NewsAutoScanService {
    @Autowired
    private GreenTextScan greenTextScan;
    @Autowired
    private GreenImageScan greenImageScan;
    @Autowired
    private NewsMapper newsMapper;
    @Autowired
    private ArticleClient articleClient;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ChannelMapper channelMapper;
    @Autowired
    private FileStorageService fileStorageService;
    @Autowired
    private SensitiveMapper sensitiveMapper;
    @Autowired
    private Tess4jClient tess4jClient;
    @Autowired
    private KafkaTemplate kafkaTemplate;

    @Override
    @Async
    public boolean autoScan(News news) {
        log.info("开始进行内容安全检测");
        if(news.getNewsStatus() != News.NewsStatus.SUBMIT.getCode()){
            log.info("文章状态不是审核，不走后续流程");
            return false;
        }
        //提取文本和图片
        Map<String, Object> contentAndImage = getContentAndImage(news);
        //自定义敏感词审核
        boolean flag = scanSensitive(news,contentAndImage.get("content") + "");
        if(!flag){
            log.info("文章 {} 存在敏感词，不走后续流程",news.getNewsId());
            return false;
        }
        //审核文本
        flag = scanText(contentAndImage.get("content") + "", news);
        if(!flag){
            log.info("文章 {}文本审核未通过，不走后续流程",news.getNewsId());
            return false;
        }
        //审核图片
        flag = scanImages((List<String>) contentAndImage.get("images"), news);
        if(!flag){
            log.info("文章 {}图片审核未通过，不走后续流程",news.getNewsId());
            return false;
        }
        //保存到APP端
        R r = saveToApp(news);
        log.info("调用文章微服务返回：{}", JSON.toJSONString(r));
        if(r.getCode() != ResultCodeRecord.SUCCESS.code()){
            log.info("同步失败");
            return false;
        }
        //更新数据
        Long articleId = Long.valueOf(r.getData().toString());
        news.setArticleId(articleId);
        news.setNewsStatus(News.NewsStatus.PUBLISHED.getCode());
        newsMapper.updateById(news);

        //发送消息通知
        kafkaTemplate.send(ArticleConstants.TOPIC_CREATE_DETAIL_HTML,articleId + "");
        return true;
    }

    public R saveToApp(News news) {
        // 同步文章到App端
        ArticleDto articleDto = new ArticleDto();
        BeanUtils.copyProperties(news, articleDto);
        // 设置布局
        articleDto.setArticleLayout(news.getArticleType());
        // 设置作者信息
        articleDto.setAuthorId(news.getUserId());
        User user = userMapper.selectById(news.getUserId());
        articleDto.setAuthorName(user == null ? null : user.getNickName());
        // 如果聚道名称
        Channel channel = channelMapper.selectById(news.getChannelId());
        articleDto.setChannelName(channel == null ? null : channel.getChannelName());

        articleDto.setPublishTime(new Date());
        articleDto.setCreatedTime(new Date());

        // 设置文章id，修改需要
        articleDto.setArticleId(news.getArticleId());

        R r = articleClient.addArticle(articleDto);
        if (r.getCode() == ResultCodeRecord.SUCCESS.code()) {
            // 更新状态及文章id
            news.setNewsStatus(News.NewsStatus.PUBLISHED.getCode());
            news.setArticleId((long) r.getData().size());
            newsMapper.updateById(news);
        }
        return r;
    }

    /***
     * 自定义敏感词审核
     * @param news
     * @param content
     * @return
     */
    private boolean scanSensitive(News news, String content) {
        boolean flag = true;
        //查询所有敏感词
        List< Sensitive > wmSensitives = sensitiveMapper.selectList(Wrappers.emptyWrapper());
        if (CollectionUtils.isEmpty(wmSensitives)) {
            log.info("未定义敏感词，不做审核");
            return flag;
        }
        List< String > sensitveList = wmSensitives.stream().map(Sensitive::getSensitives).collect(Collectors.toList());
        SensitiveWordUtil.initMap(sensitveList);
        Map< String, Integer > result = SensitiveWordUtil.matchWords(content);
        if (!CollectionUtils.isEmpty(result)) {
            log.info("敏感词审核未通过：{}", result);
            news.setNewsStatus(News.NewsStatus.FAIL.getCode());
            news.setNewsReason("文章中存在违规内容：" + result);
            newsMapper.updateById(news);
            flag = false;
        }
        return flag;
    }

    private boolean scanText(String content, News news) {
        boolean flag = true;
        try {
            Map map = greenTextScan.greeTextScan(content);
            return dealResult(map, news);
        }catch (Exception e){
            flag = false;
            log.error("阿里云文本审核异常",e);
        }
        return flag;
    }

    private boolean scanImages(List< String > contentImages, News news) {
        boolean flag = true;
        if (CollectionUtils.isEmpty(contentImages)) {
            log.info("没有图片");
            return flag;
        }
        try {
            List< byte[] > imageList = new ArrayList<>();
            for (String image : contentImages) {
                byte[] bytes = fileStorageService.downLoadFile(image);
                //识别图片中文字，进行敏感词审核
                String picText = tess4jClient.doOCR(ImageIO.read(new ByteArrayInputStream(bytes)));
                boolean sensitiveFlag = scanSensitive(news, picText);
                if (!sensitiveFlag) {
                    return false;
                }
                imageList.add(bytes);
            }
            Map map = greenImageScan.imageScan(imageList);
            return dealResult(map, news);
        } catch (Exception e) {
            log.error("图片审核异常", e);
            flag = false;
        }
        return flag;
    }

    /**
     * 结果处理
     *
     * @param result
     * @param news
     * @return
     */
    private boolean dealResult(Map result, News news) {
        boolean flag = true;
        if (result == null) {
            return flag;
        }
        String suggestion = result.get("suggestion") + "";
        String label = result.get("label") + "";
        if ("pass".equals(suggestion)) {
            return flag;
        }
        //处理失败和人工
        flag = false;
        if ("block".equals(suggestion)) {
            //更新数据库
            news.setNewsStatus(News.NewsStatus.FAIL.getCode());
            news.setNewsReason("文本或者图片中包含违法内容：" + label);
        } else if ("review".equals(suggestion)) {
            //更新数据库
            news.setNewsStatus(News.NewsStatus.ADMIN_AUTH.getCode());
            news.setNewsReason("文本或图片中包含不确定内容：" + label);
        }
        newsMapper.updateById(news);
        return flag;
    }

    /**
     * 提取文章图片和内容
     *
     * @param news
     * @return
     */
    private Map<String,Object> getContentAndImage(News news) {

        Map<String,Object> resultMap = new HashMap<>();

        StringBuilder stringBuilder = new StringBuilder();
        String content = news.getNewsContent();
        //图片
        List<String> images = new ArrayList<>();

        List<Map> contentList = JSONArray.parseArray(content, Map.class);
        for (Map map : contentList) {
            String type = map.get("type") + "";
            String value = map.get("value") + "";
            if("text".equals(type)){
                stringBuilder.append(value);
            }else if("image".equals(type)){
                images.add(value);
            }
        }
        //处理封面图片 1,2,3
        String coverImages = news.getNewsImages();
        if(!StringUtils.isEmpty(coverImages)) {
            String[] imgs = StringUtils.commaDelimitedListToStringArray(coverImages);
            images.addAll(Arrays.asList(imgs));
        }
        images.stream().distinct().collect(Collectors.toList());

        //处理标题
        stringBuilder.append("-").append(news.getNewsTitle());

        resultMap.put("content",stringBuilder.toString());
        resultMap.put("images",images);

        return resultMap;
    }
}
